package com.dayatang.hrm.compensation.domain;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import com.dayatang.commons.domain.AbstractEntity;
import com.dayatang.commons.domain.InstanceFactory;
import com.dayatang.hrm.compensation.context.CompensationMessageSource;
import com.dayatang.hrm.compensation.domain.exception.CompensationRuntimeException;
import com.dayatang.hrm.compensation.utils.DateVersionUtils;
import com.dayatang.utils.DateUtils;

@Entity
@Table(name = "pay_salary_accountsuite_setting")
@NamedQueries( {
		@NamedQuery(name = "SalaryAccountSuiteSetting.get", query = "select setting from SalaryAccountSuiteSetting setting inner join setting.salaryAccountSuite o where (o = :pSuite and setting.fromDate <= :pQueryDate and setting.toDate >= :pQueryDate)"),
		@NamedQuery(name = "SalaryAccountSuiteSetting.isNameAlreadyUsedInSameOrganizationExceptSelf.selfIsNull", query = "select setting from SalaryAccountSuiteSetting setting inner join setting.salaryAccountSuite suite inner join suite.org org where (org = :pOrg and suite.abolishDate >= :pFromDate and  ((setting.fromDate <= :pToDate and setting.toDate >= :pToDate ) or (setting.toDate >= :pFromDate and setting.toDate <= :pToDate)))"),
		@NamedQuery(name = "SalaryAccountSuiteSetting.isNameAlreadyUsedInSameOrganizationExceptSelf.selfIsExist", query = "select setting from SalaryAccountSuiteSetting setting inner join setting.salaryAccountSuite suite inner join suite.org org where (org = :pOrg and suite.abolishDate >= :pFromDate and setting <> :pSelf and ((setting.fromDate <= :pToDate and setting.toDate >= :pToDate ) or (setting.toDate >= :pFromDate and setting.toDate <= :pToDate)))") })
public class SalaryAccountSuiteSetting extends AbstractEntity {

	/**
	 * 
	 */
	private static final long serialVersionUID = 8902575887625081279L;

	private static final Logger logger = LoggerFactory
			.getLogger(SalaryAccountSuiteSetting.class);

	private static SalaryAccountSuiteSettingRepository salaryAccountSuiteSettingRepository;

	protected static MessageSourceAccessor messages = CompensationMessageSource
			.getAccessor();

	@Column(name = "accountsuite_name")
	private String name;

	@Column(name = "is_need_tag_version")
	private Boolean needTagVersion;

	@Column(name = "from_date", nullable = false)
	@Temporal(TemporalType.TIMESTAMP)
	private Date fromDate;

	@Column(name = "to_date")
	@Temporal(TemporalType.TIMESTAMP)
	private Date toDate = DateUtils.MAX_DATE;

	// 创建时间
	@Column(name = "created_time")
	@Temporal(TemporalType.TIMESTAMP)
	private Date created;

	@ManyToOne
	@JoinColumn(name = "salary_accountsuite_id")
	private SalaryAccountSuite salaryAccountSuite;

	public SalaryAccountSuiteSetting() {
	}

	public SalaryAccountSuiteSetting(SalaryAccountSuite salaryAccountSuite) {
		this.salaryAccountSuite = salaryAccountSuite;
	}

	public static SalaryAccountSuiteSettingRepository getSalaryAccountSuiteSettingRepository() {
		if (salaryAccountSuiteSettingRepository == null) {
			salaryAccountSuiteSettingRepository = InstanceFactory
					.getInstance(SalaryAccountSuiteSettingRepository.class);
		}
		return salaryAccountSuiteSettingRepository;
	}

	public static void setSalaryAccountSuiteSettingRepository(
			SalaryAccountSuiteSettingRepository salaryAccountSuiteSettingRepository) {
		SalaryAccountSuiteSetting.salaryAccountSuiteSettingRepository = salaryAccountSuiteSettingRepository;
	}

	public static MessageSourceAccessor getMessages() {
		return messages;
	}

	public static void setMessages(MessageSourceAccessor messages) {
		SalaryAccountSuiteSetting.messages = messages;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Boolean isNeedTagVersion() {
		return needTagVersion;
	}

	public Boolean getNeedTagVersion() {
		return needTagVersion;
	}

	public void setNeedTagVersion(Boolean needTagVersion) {
		this.needTagVersion = needTagVersion;
	}

	public Date getFromDate() {
		return fromDate;
	}

	public void setFromDate(Date fromDate) {
		this.fromDate = fromDate;
	}

	public Date getToDate() {
		return toDate;
	}

	public void setToDate(Date toDate) {
		this.toDate = toDate;
	}

	public Date getCreated() {
		return created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	public SalaryAccountSuite getSalaryAccountSuite() {
		return salaryAccountSuite;
	}

	public void setSalaryAccountSuite(SalaryAccountSuite salaryAccountSuite) {
		this.salaryAccountSuite = salaryAccountSuite;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof SalaryAccountSuiteSetting == false) {
			return false;
		}
		if (this == obj) {
			return true;
		}
		SalaryAccountSuiteSetting rhs = (SalaryAccountSuiteSetting) obj;
		return new EqualsBuilder().append(getId(), rhs.getId()).isEquals();
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder(17, 37).append(getId()).toHashCode();
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this,
				ToStringStyle.SHORT_PREFIX_STYLE);
	}

	/*
	 * =======================================
	 * 
	 * action
	 * 
	 * ========================================
	 */

	private void save() {
		if (isNew()) {
			setCreated(new Date());

			if (fromDate == null) {
				setFromDate(created);
			}

			if (logger.isDebugEnabled()) {
				logger.debug("准备新增设置：name【{}】，fromDate【{}】，toDate【{}】",
						new Object[] { name, fromDate, toDate });
			}

			validateName();
			getSalaryAccountSuiteSettingRepository().save(this);
		} else {
			if (isNeedTagVersion()) {
				Date markedDate = new Date();

				// 取出原有设置
				SalaryAccountSuiteSetting setting = getPure(this);
				// 设置结束日期（MySQL不能精确到毫秒）
				setting.setToDate(DateVersionUtils
						.getPreviousVersionTimestamp(markedDate));
				// 不需要验证名称
				// validateName();
				if (logger.isDebugEnabled()) {
					logger
							.debug("准备备份原有设置：name【{}】，fromDate【{}】，toDate【{}】",
									new Object[] { setting.getName(),
											setting.getFromDate(),
											setting.getToDate() });
				}
				getSalaryAccountSuiteSettingRepository().save(setting);

				// 作为新对象保存
				SalaryAccountSuiteSetting newInstance = new SalaryAccountSuiteSetting(
						salaryAccountSuite);
				BeanUtils.copyProperties(this, newInstance);
				newInstance.setId(null);
				newInstance.setFromDate(null);
				newInstance.setVersion(0);
				newInstance.setCreated(DateVersionUtils
						.getCurrentlyVersionTimestamp(markedDate));
				newInstance.save();
			} else {

				if (logger.isDebugEnabled()) {
					logger.debug(
							"准备更正设置（不备份）：name【{}】，fromDate【{}】，toDate【{}】",
							new Object[] { name, fromDate, toDate });
				}

				validateName();
				getSalaryAccountSuiteSettingRepository().save(this);
			}
		}
	}

	public void update() {
		if (isNew()) {
			throw new CompensationRuntimeException(messages.getMessage(
					"被更新的薪资帐套设置不存在。",
					"SalaryAccountSuiteSetting.update.entityNotExist"));
		}
		validateName();
		getSalaryAccountSuiteSettingRepository().save(this);
	}

	/**
	 * 改正设置并保存（不会将设置备份）
	 */
	public void saveChanges() {
		// setNeedTagVersion(false);
		save();
	}

	/**
	 * 备份原有设置并保存当前设置（会将之前的设置备份）
	 */
	public void archive() {
		setNeedTagVersion(true);
		save();
	}

	public void markArchive() {
		setNeedTagVersion(true);
		update();
	}

	public void abolish(Date abolishDate) {
		if (abolishDate == null) {
			abolishDate = new Date();
		}
		setToDate(abolishDate);
		save();
	}

	/**
	 * 验证薪资帐套名称有效性
	 * 
	 * @param name
	 * @param fromDate
	 * @param toDate
	 */
	public void validateName() {
		Assert.notNull(name, messages.getMessage(
				"SalaryAccountSuiteSetting.validateName.nameIsNull",
				"薪资帐套的名称不允许为空。"));

		if (isNameAlreadyUsedInSameOrganization()) {
			throw new CompensationRuntimeException(messages.getMessage(
					"SalaryAccountSuiteSetting.validateName.nameAlreadyUsed",
					"该薪资帐套的名称已经被使用。"));
		}
	}

	/**
	 * 在同一个机构下，薪资帐套名称是否被使用
	 * 
	 * @param salaryAccountSuite
	 * @param fromDate
	 * @param toDate
	 * @return
	 */
	public boolean isNameAlreadyUsedInSameOrganization() {
		if (isNew()) {
			return getSalaryAccountSuiteSettingRepository()
					.isNameAlreadyUsedInSameOrganizationExceptSelf(
							salaryAccountSuite.getName(),
							salaryAccountSuite.getOrg(), fromDate, toDate, null);
		} else {
			return getSalaryAccountSuiteSettingRepository()
					.isNameAlreadyUsedInSameOrganizationExceptSelf(
							salaryAccountSuite.getName(),
							salaryAccountSuite.getOrg(), fromDate, toDate, this);
		}
	}

	public static SalaryAccountSuiteSetting init(SalaryAccountSuite accountSuite) {
		return new SalaryAccountSuiteSetting(accountSuite);
	}

	public static SalaryAccountSuiteSetting get(Long id) {
		return getSalaryAccountSuiteSettingRepository().get(id);
	}

	public static SalaryAccountSuiteSetting get(
			SalaryAccountSuite accountSuite, Date queryDate) {
		return getSalaryAccountSuiteSettingRepository().get(accountSuite,
				queryDate);
	}

	public static SalaryAccountSuiteSetting getPure(
			SalaryAccountSuiteSetting salaryAccountSuiteSetting) {
		return getSalaryAccountSuiteSettingRepository().getPure(
				salaryAccountSuiteSetting);
	}

}
